package com.kevin.utility.db;

import java.beans.PropertyVetoException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;

import javax.sql.DataSource;

import org.apache.log4j.Logger;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.kevin.utility.enums.Separator;

/**
 * @author Random.LJ
 * 实现Spring提供的接口，每当开启新事务的时候自动调用
 *
 */
public class DynamicDataSource extends AbstractRoutingDataSource {
	
	private final static Logger logger = Logger.getLogger(DynamicDataSource.class);
	private Map<Object, Object> targetDataSources = new ConcurrentHashMap<Object, Object>();

	@Override
	protected Object determineCurrentLookupKey() {
		String dataSourceKey = DbContextHolder.getDbType();
		if (!DbContextHolder.MASTER.equals(dataSourceKey) && !targetDataSources.containsKey(dataSourceKey)) {
			synchronized (DynamicDataSource.class) {
				String[] info = dataSourceKey.split(Separator.AND.getText());
				ComboPooledDataSource dataSource = createDataSource(info[0], info[1], info[2], info[3]);
				this.addTargetDataSource(dataSourceKey, dataSource);
			}
		}
		return dataSourceKey;
	}
	
	
	@Override
	public void setTargetDataSources(Map<Object, Object> targetDataSources) {
		for(Entry<Object, Object> entry:targetDataSources.entrySet()){
			this.targetDataSources.put(entry.getKey(), entry.getValue());
		}
		super.setTargetDataSources(this.targetDataSources);
	}
	
	public void addTargetDataSource(String key, ComboPooledDataSource dataSource) {
		this.targetDataSources.put(key,  dataSource);
		super.setTargetDataSources(targetDataSources);
		afterPropertiesSet();
	}
	
	public ComboPooledDataSource createDataSource(String driverClassName, String url, String username, String password) {
		ComboPooledDataSource dataSource = new ComboPooledDataSource();
		try {
			dataSource.setDriverClass(driverClassName);
			dataSource.setJdbcUrl(url);
			dataSource.setUser(username);
			dataSource.setPassword(password);
			
			//连接池在无空闲连接可用时一次性创建的新数据库连接数,default : 3
			dataSource.setAcquireIncrement(5);
			
			//连接池初始化时创建的连接数,default : 3，取值应在minPoolSize与maxPoolSize之间
			dataSource.setInitialPoolSize(3);
			
			//连接的最大空闲时间，如果超过这个时间，某个数据库连接还没有被使用，则会断开掉这个连接。如果为0，则永远不会断开连接,即回收此连接。default : 0 单位 s
			dataSource.setMaxIdleTime(60);
			
			//连接池保持的最小连接数,default : 3
			dataSource.setMinPoolSize(1);
			
			//连接池中拥有的最大连接数，如果获得新连接时会使连接总数超过这个值则不会再获取新连接，而是等待其他连接释放，所以这个值有可能会设计地很大,default : 15
			dataSource.setMaxPoolSize(500);
			
			//每900秒检查所有连接池中的空闲连接
			//dataSource.setIdleConnectionTestPeriod(900);
			
			//配置PreparedStatement缓存
			//连接池为数据源缓存的PreparedStatement的总数。由于PreparedStatement属于单个Connection,所以这个数量应该根据应用中平均连接数乘以每个连接的平均PreparedStatement来计算。同时maxStatementsPerConnection的配置无效。default : 0（不建议使用）
			//dataSource.setMaxStatements(500);

			//连接池为数据源单个Connection缓存的PreparedStatement数，这个配置比maxStatements更有意义，因为它缓存的服务对象是单个数据连接，
			//如果设置的好，肯定是可以提高性能的。为0的时候不缓存。default : 0（看情况而论）
			//dataSource.setMaxStatementsPerConnection(30);
			
			//连接池在获得新连接失败时重试的次数，如果小于等于0则无限重试直至连接获得成功。default : 30（建议使用）
			dataSource.setAcquireRetryAttempts(3);
			
			//两次连接中间隔时间，单位毫秒，连接池在获得新连接时的间隔时间。default : 1000 单位ms（建议使用）
			dataSource.setAcquireRetryDelay(1000);
			
			//如果为true，则当连接获取失败时自动关闭数据源，除非重新启动应用程序。所以一般不用。default : false（不建议使用）
			dataSource.setBreakAfterAcquireFailure(false);
		} catch (PropertyVetoException e) {
			logger.error(e);
		}
		return dataSource;
	}
}
